Skip to content

Change creator_id to only need to be unique for schools where rejecte…#742

Merged
jamiebenstead merged 6 commits intomainfrom
1233-creators-of-previously-rejected-schools-cant-sign-up
Mar 24, 2026
Merged

Change creator_id to only need to be unique for schools where rejecte…#742
jamiebenstead merged 6 commits intomainfrom
1233-creators-of-previously-rejected-schools-cant-sign-up

Conversation

@jamiebenstead
Copy link
Copy Markdown
Contributor

Status

What's changed?

  • Update school creation validation so that creator_id can be reused if rejected_at is not nil

Copilot AI review requested due to automatic review settings March 20, 2026 14:15
@cla-bot cla-bot bot added the cla-signed label Mar 20, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 20, 2026

Test coverage

89.63% line coverage reported by SimpleCov.
Run: https://github.com/RaspberryPiFoundation/editor-api/actions/runs/23482549745

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the School model so a creator_id only needs to be unique among non-rejected schools, enabling a user to create a new school after their previous one was rejected.

Changes:

  • Scope creator_id uniqueness validation to records where rejected_at is nil.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +2 to +3
def change
remove_index :schools, name: "index_schools_on_creator_id"
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove_index is called by name: inside a change migration, which makes the migration irreversible on rollback (Rails can’t reconstruct the removed index definition from the name alone). Consider switching to an explicit up/down (or reversible) that re-adds the original unique index on creator_id in down, and/or use remove_index :schools, :creator_id so Rails can invert it.

Copilot uses AI. Check for mistakes.
:creator_id,
unique: true,
where: "rejected_at IS NULL",
name: "index_schools_on_creator_id_active_only"
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new partial index is given a non-standard name (index_schools_on_creator_id_active_only). For consistency with the existing partial unique indexes on schools (e.g. index_schools_on_reference), consider reusing the conventional name index_schools_on_creator_id for the replacement index to reduce future migration complexity.

Suggested change
name: "index_schools_on_creator_id_active_only"
name: "index_schools_on_creator_id"

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@zetter-rpf zetter-rpf Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a preference either way. I don't see any problem with the more descriptive name as you only tend to refer to index names from within other migrations. I think there is a limit to the name length but this would likely be within it.

I would choose what you think is clearer.

Comment on lines +34 to +38
validates :creator_id,
presence: true,
uniqueness: {
conditions: -> { where(rejected_at: nil) }
}
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test coverage: this changes creator_id uniqueness semantics to allow reuse when the existing school is rejected. Please add/adjust specs to cover the new behavior (duplicate creator_id allowed when the other record has rejected_at set, but still rejected when both are active).

Copilot uses AI. Check for mistakes.
Comment on lines 59 to 61
def self.find_for_user!(user)
school = Role.find_by(user_id: user.id)&.school || find_by(creator_id: user.id)
school = Role.find_by(user_id: user.id)&.school || find_by(creator_id: user.id, rejected_at: nil)
raise ActiveRecord::RecordNotFound unless school
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test coverage: .find_for_user! now excludes schools created by the user if they are rejected (rejected_at set). Add a spec for the rejected-school case (e.g., creator has only a rejected school -> RecordNotFound, and creator has both rejected + active -> returns the active one).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +13 to +18
remove_index :schools, name: "index_schools_on_creator_id_active_only"

add_index :schools,
:creator_id,
unique: true,
name: "index_schools_on_creator_id"
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The down migration recreates a global unique index on creator_id. After this change is deployed, the table may legitimately contain multiple rows with the same creator_id (as long as only one is active), so rolling back would fail when re-adding this index. Consider marking the migration as irreversible, or adding a rollback strategy (e.g., clean up/merge duplicate rows or raise with a clear message before attempting to add the unique index).

Suggested change
remove_index :schools, name: "index_schools_on_creator_id_active_only"
add_index :schools,
:creator_id,
unique: true,
name: "index_schools_on_creator_id"
raise ActiveRecord::IrreversibleMigration,
"Cannot safely restore global unique index on creator_id because " \
"multiple rows per creator_id may exist after this migration."

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This risk here is that if we did roll back, and the remove_index succeeded, but the add_index didn't, we would could get duplicates.

This is a small risk as it only applies if we decide to manually roll back the code and migrations so I don't have a strong preference as to if it's worth changing or not - up to you.

Comment on lines 59 to 61
def self.find_for_user!(user)
school = Role.find_by(user_id: user.id)&.school || find_by(creator_id: user.id)
school = Role.find_by(user_id: user.id)&.school || find_by(creator_id: user.id, rejected_at: nil)
raise ActiveRecord::RecordNotFound unless school
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change introduces new behavior (creator_id can be reused when the existing school is rejected, and .find_for_user! should ignore rejected schools). There are existing model specs for creator_id uniqueness and .find_for_user! (spec/models/school_spec.rb), but they don't appear to cover the rejected-school cases. Please add/adjust tests to assert that a second school with the same creator_id is valid when the first has rejected_at set, and that .find_for_user! returns the active school (or raises) when only a rejected school exists.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@zetter-rpf zetter-rpf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice one

@jamiebenstead jamiebenstead merged commit c1f5481 into main Mar 24, 2026
6 checks passed
@jamiebenstead jamiebenstead deleted the 1233-creators-of-previously-rejected-schools-cant-sign-up branch March 24, 2026 10:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants